home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkWm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-21  |  62.8 KB  |  2,101 lines

  1. /* 
  2.  * tkWm.c --
  3.  *
  4.  *    This module takes care of the interactions between a Tk-based
  5.  *    application and the window manager.  Among other things, it
  6.  *    implements the "wm" command and passes geometry information
  7.  *    to the window manager.
  8.  *
  9.  * Copyright 1991 Regents of the University of California.
  10.  * Permission to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose and without
  12.  * fee is hereby granted, provided that the above copyright
  13.  * notice appear in all copies.  The University of California
  14.  * makes no representations about the suitability of this
  15.  * software for any purpose.  It is provided "as is" without
  16.  * express or implied warranty.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkWm.c,v 1.32 92/08/21 16:26:31 ouster Exp $ SPRITE (Berkeley)";
  21. #endif
  22.  
  23. #include "tkConfig.h"
  24. #include "tkInt.h"
  25.  
  26. /*
  27.  * The definitions below compensate for the lack of some definitions
  28.  * under X11R3.
  29.  */
  30.  
  31. #ifdef X11R3
  32. #define PBaseSize    (1L<<8)
  33. #endif
  34.  
  35. /*
  36.  * A data structure of the following type holds window-manager-related
  37.  * information for each top-level window in an application.
  38.  */
  39.  
  40. typedef struct TkWmInfo {
  41.     TkWindow *winPtr;        /* Pointer to main Tk information for
  42.                  * this window. */
  43.     Window reparent;        /* If the window has been reparented, this
  44.                  * gives the ID of the ancestor of the window
  45.                  * that is a child of the root window (may
  46.                  * not be window's immediate parent).  If
  47.                  * the window isn't reparented, this has the
  48.                  * value None. */
  49.     Tk_Uid titleUid;        /* Title to display in window caption.  If
  50.                  * NULL, use name of widget. */
  51.     Tk_Uid iconName;        /* Name to display in icon. */
  52.     Window master;        /* Master window for TRANSIENT_FOR property,
  53.                  * or None. */
  54.     XWMHints hints;        /* Various pieces of information for
  55.                  * window manager. */
  56.     Tk_Uid leaderName;        /* Path name of leader of window group
  57.                  * (corresponds to hints.window_group).
  58.                  * Note:  this field doesn't get updated
  59.                  * if leader is destroyed. */
  60.     Tk_Uid iconWindowName;    /* Path name of window specified as icon
  61.                  * window for this window, or NULL.  Note:
  62.                  * this field doesn't get updated if
  63.                  * iconWindowName is destroyed. */
  64.     Tk_Uid masterWindowName;    /* Path name of window specified as master
  65.                  * in "wm transient" command, or NULL.
  66.                  * Note:  this field doesn't get updated if
  67.                  * masterWindowName is destroyed. */
  68.  
  69.     /*
  70.      * Information used to construct an XSizeHints structure for
  71.      * the window manager:
  72.      */
  73.  
  74.     int sizeHintsFlags;        /* Flags word for XSizeHints structure.
  75.                  * If the PBaseSize flag is set then the
  76.                  * window is gridded;  otherwise it isn't
  77.                  * gridded. */
  78.     int minWidth, minHeight;    /* Minimum dimensions of window, in
  79.                  * grid units, not pixels. */
  80.     int maxWidth, maxHeight;    /* Maximum dimensions of window, in
  81.                  * grid units, not pixels. */
  82.     int widthInc, heightInc;    /* Increments for size changes (# pixels
  83.                  * per step). */
  84.     struct {
  85.     int x;    /* numerator */
  86.     int y;  /* denominator */
  87.     } minAspect, maxAspect;    /* Min/max aspect ratios for window. */
  88.     int reqGridWidth, reqGridHeight;
  89.                 /* The dimensions of the window (in
  90.                  * grid units) requested through
  91.                  * the geometry manager. */
  92.     int gravity;        /* Desired window gravity. */
  93.  
  94.     /*
  95.      * Information used to manage the size and location of a window.
  96.      */
  97.  
  98.     int prevReqWidth, prevReqHeight;
  99.                 /* Last known size preferences, as specified
  100.                  * to Tk_GeometryRequest.  Used to tell when
  101.                  * the preferred dimensions have changed. */
  102.     int width, height;        /* Desired dimensions of window, specified
  103.                  * in grid units.  These values are
  104.                  * set by the "wm geometry" command and by
  105.                  * ConfigureNotify events (for when wm
  106.                  * resizes window).  -1 means user hasn't
  107.                  * requested dimensions. */
  108.     int x, y;            /* Desired X and Y coordinates for window.
  109.                  * These values are set by "wm geometry",
  110.                  * plus by ConfigureNotify events (when wm
  111.                  * moves window).  These numbers are
  112.                  * different than the numbers stored in
  113.                  * winPtr->changes because (a) they could be
  114.                  * measured from the right or bottom edge
  115.                  * of the screen (see WM_NEGATIVE_X and
  116.                  * WM_NEGATIVE_Y flags) and (b) if the window
  117.                  * has been reparented then they refer to the
  118.                  * parent rather than the window itself. */
  119.     int parentWidth, parentHeight;
  120.                 /* Width and height of reparent, in pixels
  121.                  * *including border*.  If window hasn't been
  122.                  * reparented then these will be the outer
  123.                  * dimensions of the window, including
  124.                  * border. */
  125.     int xInParent, yInParent;    /* Offset of window within reparent,  measured
  126.                  * from upper-left outer corner of parent's
  127.                  * border.  If not reparented then these are
  128.                  * zero. */
  129.     unsigned long configRequest;/* Serial number of last request that we
  130.                  * issued to change geometry of window.
  131.                  * Used to discard configure events that
  132.                  * we know will be superceded. */
  133.     int configWidth, configHeight;
  134.                 /* Dimensions passed to last request that we
  135.                  * issued to change geometry of window.  Used
  136.                  * to eliminate redundant resize operations. */
  137.  
  138.     int flags;            /* Miscellaneous flags, defined below. */
  139.     struct TkWmInfo *nextPtr;    /* Next in list of all top-level windows. */
  140. } WmInfo;
  141.  
  142. /*
  143.  * Flag values for WmInfo structures:
  144.  *
  145.  * WM_NEVER_MAPPED -        non-zero means window has never been
  146.  *                mapped;  need to update all info when
  147.  *                window is first mapped.
  148.  * WM_UPDATE_PENDING -        non-zero means a call to UpdateGeometryInfo
  149.  *                has already been scheduled for this
  150.  *                window;  no need to schedule another one.
  151.  * WM_NEGATIVE_X -        non-zero means x-coordinate is measured in
  152.  *                pixels from right edge of screen, rather
  153.  *                than from left edge.
  154.  * WM_NEGATIVE_Y -        non-zero means y-coordinate is measured in
  155.  *                pixels up from bottom of screen, rather than
  156.  *                down from top.
  157.  * WM_UPDATE_SIZE_HINTS -    non-zero means that new size hints need to be
  158.  *                propagated to window manager.
  159.  * WM_NESTED_REPARENT -        non-zero means that the window has been
  160.  *                reparented several levels deep in a hierarchy
  161.  *                (i.e. reparent isn't the window's immediate
  162.  *                parent).
  163.  * WM_CONFIG_PENDING -        non-zero means we've asked for the top-level
  164.  *                window to be resized but haven't seen a
  165.  *                ConfigureNotify event to indicate that the
  166.  *                resize occurred.
  167.  * WM_CONFIG_AGAIN -        non-zero means we need to reconfigure the
  168.  *                window again as soon as the current configure
  169.  *                request has been processed by the window
  170.  *                manager.
  171.  */
  172.  
  173. #define WM_NEVER_MAPPED        1
  174. #define WM_UPDATE_PENDING    2
  175. #define WM_NEGATIVE_X        4
  176. #define WM_NEGATIVE_Y        8
  177. #define WM_UPDATE_SIZE_HINTS    0x10
  178. #define WM_NESTED_REPARENT    0x20
  179. #define WM_CONFIG_PENDING    0x40
  180. #define WM_CONFIG_AGAIN        0x100
  181.  
  182. /*
  183.  * This module keeps a list of all top-level windows, primarily to
  184.  * simplify the job of Tk_CoordsToWindow.
  185.  */
  186.  
  187. static WmInfo *firstWmPtr = NULL;    /* Points to first top-level window. */
  188.  
  189. #define IS_GRIDDED(wmPtr) ((wmPtr)->sizeHintsFlags & PBaseSize)
  190.  
  191. /*
  192.  * Forward declarations for procedures defined in this file:
  193.  */
  194.  
  195. static int        ParseGeometry _ANSI_ARGS_ ((Tcl_Interp *interp,
  196.                 char *string, TkWindow *winPtr));
  197. static void        TopLevelEventProc _ANSI_ARGS_((ClientData clientData,
  198.                 XEvent *eventPtr));
  199. static void        TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
  200.                 Tk_Window tkwin));
  201. static void        UpdateGeometryInfo _ANSI_ARGS_((
  202.                 ClientData clientData));
  203. static void        UpdateHints _ANSI_ARGS_((TkWindow *winPtr));
  204. static void        UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr));
  205.  
  206. /*
  207.  *--------------------------------------------------------------
  208.  *
  209.  * TkWmNewWindow --
  210.  *
  211.  *    This procedure is invoked whenever a new top-level
  212.  *    window is created.  Its job is to initialize the WmInfo
  213.  *    structure for the window.
  214.  *
  215.  * Results:
  216.  *    None.
  217.  *
  218.  * Side effects:
  219.  *    A WmInfo structure gets allocated and initialized.
  220.  *
  221.  *--------------------------------------------------------------
  222.  */
  223.  
  224. void
  225. TkWmNewWindow(winPtr)
  226.     TkWindow *winPtr;        /* Newly-created top-level window. */
  227. {
  228.     register WmInfo *wmPtr;
  229.  
  230.     wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
  231.     wmPtr->winPtr = winPtr;
  232.     wmPtr->reparent = None;
  233.     wmPtr->titleUid = NULL;
  234.     wmPtr->iconName = NULL;
  235.     wmPtr->master = None;
  236.     wmPtr->hints.flags = InputHint | StateHint;
  237.     wmPtr->hints.input = True;
  238.     wmPtr->hints.initial_state = NormalState;
  239.     wmPtr->hints.icon_pixmap = None;
  240.     wmPtr->hints.icon_window = None;
  241.     wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
  242.     wmPtr->hints.icon_mask = None;
  243.     wmPtr->hints.window_group = None;
  244.     wmPtr->leaderName = NULL;
  245.     wmPtr->iconWindowName = NULL;
  246.     wmPtr->masterWindowName = NULL;
  247.     wmPtr->sizeHintsFlags = 0;
  248.     wmPtr->minWidth = wmPtr->minHeight = 0;
  249.     wmPtr->maxWidth = wmPtr->maxHeight = 10000;
  250.     wmPtr->widthInc = wmPtr->heightInc = 1;
  251.     wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
  252.     wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
  253.     wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
  254.     wmPtr->prevReqWidth = wmPtr->prevReqHeight = -1;
  255.     wmPtr->gravity = NorthWestGravity;
  256.     wmPtr->width = -1;
  257.     wmPtr->height = -1;
  258.     wmPtr->x = winPtr->changes.x;
  259.     wmPtr->y = winPtr->changes.y;
  260.     wmPtr->parentWidth = winPtr->changes.width
  261.         + 2*winPtr->changes.border_width;
  262.     wmPtr->parentHeight = winPtr->changes.height
  263.         + 2*winPtr->changes.border_width;
  264.     wmPtr->xInParent = wmPtr->yInParent = 0;
  265.     wmPtr->configRequest = 0;
  266.     wmPtr->configWidth = -1;
  267.     wmPtr->configHeight = -1;
  268.     wmPtr->flags = WM_NEVER_MAPPED;
  269.     wmPtr->nextPtr = firstWmPtr;
  270.     firstWmPtr = wmPtr;
  271.     winPtr->wmInfoPtr = wmPtr;
  272.  
  273.     /*
  274.      * Tk must monitor certain events for top-level windows:
  275.      * (a) structure events, in order to detect size and position changes
  276.      *     caused by window managers.
  277.      * (b) enter/level events, in order to perform focussing correctly.
  278.      */
  279.  
  280.     Tk_CreateEventHandler((Tk_Window) winPtr,
  281.         StructureNotifyMask|EnterWindowMask|LeaveWindowMask,
  282.         TopLevelEventProc, (ClientData) winPtr);
  283.  
  284.     /*
  285.      * Arrange for geometry requests to be reflected from the window
  286.      * to the window manager.
  287.      */
  288.  
  289.     Tk_ManageGeometry((Tk_Window) winPtr, TopLevelReqProc, (ClientData) 0);
  290. }
  291.  
  292. /*
  293.  *--------------------------------------------------------------
  294.  *
  295.  * TkWmMapWindow --
  296.  *
  297.  *    This procedure is invoked just before a top-level window
  298.  *    is mapped.  It gives this module a chance to update all
  299.  *    window-manager-related information in properties before
  300.  *    the window manager sees the map event and checks the
  301.  *    properties.
  302.  *
  303.  * Results:
  304.  *    Returns non-zero if it's OK for the window to be mapped, 0
  305.  *    if the caller shouldn't map the window after all (e.g. because
  306.  *    it has been withdrawn).
  307.  *
  308.  * Side effects:
  309.  *    Properties of winPtr may get updated to provide up-to-date
  310.  *    information to the window manager.
  311.  *
  312.  *--------------------------------------------------------------
  313.  */
  314.  
  315. int
  316. TkWmMapWindow(winPtr)
  317.     TkWindow *winPtr;        /* Top-level window that's about to
  318.                  * be mapped. */
  319. {
  320.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  321. #ifndef X11R3
  322.     XTextProperty textProp;
  323. #endif
  324.  
  325.     /*
  326.      * Set the MAPPED flag if the window is going to appear in its normal
  327.      * state:  if it's going to be iconified or withdrawn then it won't
  328.      * ever be mapped.
  329.      */
  330.  
  331.     if (wmPtr->hints.initial_state == NormalState) {
  332.     winPtr->flags |= TK_MAPPED;
  333.     }
  334.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  335.     return 1;
  336.     }
  337.     wmPtr->flags &= ~WM_NEVER_MAPPED;
  338.  
  339.     /*
  340.      * This is the first time this window has ever been mapped.
  341.      * Store all the window-manager-related information for the
  342.      * window.
  343.      */
  344.  
  345. #ifndef X11R3
  346.     if (wmPtr->titleUid == NULL) {
  347.     wmPtr->titleUid = winPtr->nameUid;
  348.     }
  349.     if (XStringListToTextProperty(&wmPtr->titleUid, 1, &textProp)  != 0) {
  350.     XSetWMName(winPtr->display, winPtr->window, &textProp);
  351.     XFree((char *) textProp.value);
  352.     }
  353. #endif
  354.  
  355.     TkWmSetClass(winPtr);
  356.  
  357.     if (wmPtr->iconName != NULL) {
  358.     XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
  359.     }
  360.  
  361.     if (wmPtr->master != None) {
  362.     XSetTransientForHint(winPtr->display, winPtr->window, wmPtr->master);
  363.     }
  364.  
  365.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  366.     UpdateGeometryInfo((ClientData) winPtr);
  367.     UpdateHints(winPtr);
  368.     if (wmPtr->hints.initial_state == WithdrawnState) {
  369.     return 0;
  370.     }
  371.     return 1;
  372. }
  373.  
  374. /*
  375.  *--------------------------------------------------------------
  376.  *
  377.  * TkWmDeadWindow --
  378.  *
  379.  *    This procedure is invoked when a top-level window is
  380.  *    about to be deleted.  It cleans up the wm-related data
  381.  *    structures for the window.
  382.  *
  383.  * Results:
  384.  *    None.
  385.  *
  386.  * Side effects:
  387.  *    The WmInfo structure for winPtr gets freed up.
  388.  *
  389.  *--------------------------------------------------------------
  390.  */
  391.  
  392. void
  393. TkWmDeadWindow(winPtr)
  394.     TkWindow *winPtr;        /* Newly-created top-level window. */
  395. {
  396.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  397.  
  398.     if (wmPtr == NULL) {
  399.     return;
  400.     }
  401.     if (firstWmPtr == wmPtr) {
  402.     firstWmPtr = wmPtr->nextPtr;
  403.     } else {
  404.     register WmInfo *prevPtr;
  405.  
  406.     for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) {
  407.         if (prevPtr == NULL) {
  408.         panic("couldn't unlink window in TkWmDeadWindow");
  409.         }
  410.         if (prevPtr->nextPtr == wmPtr) {
  411.         prevPtr->nextPtr = wmPtr->nextPtr;
  412.         break;
  413.         }
  414.     }
  415.     }
  416.     if (wmPtr->hints.flags & IconPixmapHint) {
  417.     Tk_FreeBitmap(wmPtr->hints.icon_pixmap);
  418.     }
  419.     if (wmPtr->hints.flags & IconMaskHint) {
  420.     Tk_FreeBitmap(wmPtr->hints.icon_mask);
  421.     }
  422.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  423.     Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  424.     }
  425.     ckfree((char *) wmPtr);
  426.     winPtr->wmInfoPtr = NULL;
  427. }
  428.  
  429. /*
  430.  *--------------------------------------------------------------
  431.  *
  432.  * TkWmSetClass --
  433.  *
  434.  *    This procedure is invoked whenever a top-level window's
  435.  *    class is changed.  If the window has been mapped then this
  436.  *    procedure updates the window manager property for the
  437.  *    class.  If the window hasn't been mapped, the update is
  438.  *    deferred until just before the first mapping.
  439.  *
  440.  * Results:
  441.  *    None.
  442.  *
  443.  * Side effects:
  444.  *    A window property may get updated.
  445.  *
  446.  *--------------------------------------------------------------
  447.  */
  448.  
  449. void
  450. TkWmSetClass(winPtr)
  451.     TkWindow *winPtr;        /* Newly-created top-level window. */
  452. {
  453.     if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
  454.     return;
  455.     }
  456.  
  457. #ifndef X11R3
  458.     if (winPtr->classUid != NULL) {
  459.     XClassHint *classPtr;
  460.  
  461.     classPtr = XAllocClassHint();
  462.     classPtr->res_name = winPtr->nameUid;
  463.     classPtr->res_class = winPtr->classUid;
  464.     XSetClassHint(winPtr->display, winPtr->window, classPtr);
  465.     XFree((char *) classPtr);
  466.     }
  467. #endif
  468. }
  469.  
  470. /*
  471.  *----------------------------------------------------------------------
  472.  *
  473.  * Tk_WmCmd --
  474.  *
  475.  *    This procedure is invoked to process the "wm" Tcl command.
  476.  *    See the user documentation for details on what it does.
  477.  *
  478.  * Results:
  479.  *    A standard Tcl result.
  480.  *
  481.  * Side effects:
  482.  *    See the user documentation.
  483.  *
  484.  *----------------------------------------------------------------------
  485.  */
  486.  
  487.     /* ARGSUSED */
  488. int
  489. Tk_WmCmd(clientData, interp, argc, argv)
  490.     ClientData clientData;    /* Main window associated with
  491.                  * interpreter. */
  492.     Tcl_Interp *interp;        /* Current interpreter. */
  493.     int argc;            /* Number of arguments. */
  494.     char **argv;        /* Argument strings. */
  495. {
  496.     Tk_Window tkwin = (Tk_Window) clientData;
  497.     TkWindow *winPtr;
  498.     register WmInfo *wmPtr;
  499.     char c;
  500.     int length;
  501.  
  502.     if (argc < 3) {
  503.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  504.         argv[0], " option window ?arg ...?\"", (char *) NULL);
  505.     return TCL_ERROR;
  506.     }
  507.     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  508.     if (winPtr == NULL) {
  509.     return TCL_ERROR;
  510.     }
  511.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  512.     Tcl_AppendResult(interp, "window \"", winPtr->pathName,
  513.         "\" isn't a top-level window", (char *) NULL);
  514.     return TCL_ERROR;
  515.     }
  516.     wmPtr = winPtr->wmInfoPtr;
  517.     c = argv[1][0];
  518.     length = strlen(argv[1]);
  519.     if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
  520.     int numer1, denom1, numer2, denom2;
  521.  
  522.     if ((argc != 3) && (argc != 7)) {
  523.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  524.             argv[0], " aspect window ?minNumer minDenom ",
  525.             "maxNumer maxDenom?\"", (char *) NULL);
  526.         return TCL_ERROR;
  527.     }
  528.     if (argc == 3) {
  529.         if (wmPtr->sizeHintsFlags & PAspect) {
  530.         sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,
  531.             wmPtr->minAspect.y, wmPtr->maxAspect.x,
  532.             wmPtr->maxAspect.y);
  533.         }
  534.         return TCL_OK;
  535.     }
  536.     if (*argv[3] == '\0') {
  537.         wmPtr->sizeHintsFlags &= ~PAspect;
  538.     } else {
  539.         if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
  540.             || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
  541.             || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
  542.             || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
  543.         return TCL_ERROR;
  544.         }
  545.         if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
  546.             (denom2 <= 0)) {
  547.         interp->result = "aspect number can't be <= 0";
  548.         return TCL_ERROR;
  549.         }
  550.         wmPtr->minAspect.x = numer1;
  551.         wmPtr->minAspect.y = denom1;
  552.         wmPtr->maxAspect.x = numer2;
  553.         wmPtr->maxAspect.y = denom2;
  554.         wmPtr->sizeHintsFlags |= PAspect;
  555.     }
  556.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  557.     goto updateGeom;
  558.     } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
  559.     if (argc != 3) {
  560.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  561.             argv[0], " deiconify window\"", (char *) NULL);
  562.         return TCL_ERROR;
  563.     }
  564.     wmPtr->hints.initial_state = NormalState;
  565.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  566.         return TCL_OK;
  567.     }
  568.     Tk_MapWindow((Tk_Window) winPtr);
  569.     } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)) {
  570.     if ((argc != 3) && (argc != 4)) {
  571.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  572.             argv[0], " focusmodel window ?active|passive?\"",
  573.             (char *) NULL);
  574.         return TCL_ERROR;
  575.     }
  576.     if (argc == 3) {
  577.         interp->result = wmPtr->hints.input ? "passive" : "active";
  578.         return TCL_OK;
  579.     }
  580.     c = argv[3][0];
  581.     length = strlen(argv[3]);
  582.     if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
  583.         wmPtr->hints.input = False;
  584.     } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
  585.         wmPtr->hints.input = True;
  586.     } else {
  587.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  588.             "\": must be active or passive", (char *) NULL);
  589.         return TCL_ERROR;
  590.     }
  591.     UpdateHints(winPtr);
  592.     } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
  593.         && (length >= 2)) {
  594.     char xSign, ySign;
  595.     int width, height;
  596.  
  597.     if ((argc != 3) && (argc != 4)) {
  598.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  599.             argv[0], " geometry window ?newGeometry?\"",
  600.             (char *) NULL);
  601.         return TCL_ERROR;
  602.     }
  603.     if (argc == 3) {
  604.         xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
  605.         ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
  606.         if (wmPtr->width != -1) {
  607.         width = wmPtr->width;
  608.         height = wmPtr->height;
  609.         } else if (IS_GRIDDED(wmPtr)) {
  610.         width = wmPtr->reqGridWidth;
  611.         height = wmPtr->reqGridHeight;
  612.         } else {
  613.         width = winPtr->reqWidth;
  614.         height = winPtr->reqHeight;
  615.         }
  616.         sprintf(interp->result, "%dx%d%c%d%c%d", width, height,
  617.             xSign, wmPtr->x, ySign, wmPtr->y);
  618.         return TCL_OK;
  619.     }
  620.     if (*argv[3] == '\0') {
  621.         wmPtr->width = -1;
  622.         wmPtr->height = -1;
  623.         goto updateGeom;
  624.     }
  625.     return ParseGeometry(interp, argv[3], winPtr);
  626.     } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
  627.         && (length >= 3)) {
  628.     int reqWidth, reqHeight, widthInc, heightInc;
  629.  
  630.     if ((argc != 3) && (argc != 7)) {
  631.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  632.             argv[0], " reqsize window ?baseWidth baseHeight ",
  633.             "widthInc heightInc?\"", (char *) NULL);
  634.         return TCL_ERROR;
  635.     }
  636.     if (argc == 3) {
  637.         if (wmPtr->sizeHintsFlags & PBaseSize) {
  638.         sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,
  639.             wmPtr->reqGridHeight, wmPtr->widthInc,
  640.             wmPtr->heightInc);
  641.         }
  642.         return TCL_OK;
  643.     }
  644.     if (*argv[3] == '\0') {
  645.         /*
  646.          * Turn off gridding and reset the width and height
  647.          * to make sense as ungridded numbers.
  648.          */
  649.  
  650.         wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
  651.         wmPtr->widthInc = 1;
  652.         wmPtr->heightInc = 1;
  653.         if (wmPtr->width != -1) {
  654.         wmPtr->width = winPtr->reqWidth + (wmPtr->width
  655.             - wmPtr->reqGridWidth)*wmPtr->widthInc;
  656.         wmPtr->height = winPtr->reqHeight + (wmPtr->height
  657.             - wmPtr->reqGridHeight)*wmPtr->heightInc;
  658.         }
  659.     } else {
  660.         if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
  661.             || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
  662.             || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
  663.             || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
  664.         return TCL_ERROR;
  665.         }
  666.         if (reqWidth < 0) {
  667.         interp->result = "baseWidth can't be < 0";
  668.         return TCL_ERROR;
  669.         }
  670.         if (reqHeight < 0) {
  671.         interp->result = "baseHeight can't be < 0";
  672.         return TCL_ERROR;
  673.         }
  674.         if (widthInc < 0) {
  675.         interp->result = "widthInc can't be < 0";
  676.         return TCL_ERROR;
  677.         }
  678.         if (heightInc < 0) {
  679.         interp->result = "heightInc can't be < 0";
  680.         return TCL_ERROR;
  681.         }
  682.         Tk_SetGrid((Tk_Window) tkwin, reqWidth, reqHeight, widthInc,
  683.             heightInc);
  684.     }
  685.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  686.     goto updateGeom;
  687.     } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
  688.         && (length >= 3)) {
  689.     Tk_Window tkwin2;
  690.  
  691.     if ((argc != 3) && (argc != 4)) {
  692.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  693.             argv[0], " group window ?pathName?\"",
  694.             (char *) NULL);
  695.         return TCL_ERROR;
  696.     }
  697.     if (argc == 3) {
  698.         if (wmPtr->hints.flags & WindowGroupHint) {
  699.         interp->result = wmPtr->leaderName;
  700.         }
  701.         return TCL_OK;
  702.     }
  703.     if (*argv[3] == '\0') {
  704.         wmPtr->hints.flags &= ~WindowGroupHint;
  705.         wmPtr->leaderName = NULL;
  706.     } else {
  707.         tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
  708.         if (tkwin2 == NULL) {
  709.         return TCL_ERROR;
  710.         }
  711.         Tk_MakeWindowExist(tkwin2);
  712.         wmPtr->hints.window_group = Tk_WindowId(tkwin2);
  713.         wmPtr->hints.flags |= WindowGroupHint;
  714.         wmPtr->leaderName = Tk_PathName(tkwin2);
  715.     }
  716.     UpdateHints(winPtr);
  717.     } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
  718.         && (length >= 5)) {
  719.     Pixmap pixmap;
  720.  
  721.     if ((argc != 3) && (argc != 4)) {
  722.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  723.             argv[0], " iconbitmap window ?bitmap?\"",
  724.             (char *) NULL);
  725.         return TCL_ERROR;
  726.     }
  727.     if (argc == 3) {
  728.         if (wmPtr->hints.flags & IconPixmapHint) {
  729.         interp->result = Tk_NameOfBitmap(wmPtr->hints.icon_pixmap);
  730.         }
  731.         return TCL_OK;
  732.     }
  733.     if (*argv[3] == '\0') {
  734.         if (wmPtr->hints.icon_pixmap != None) {
  735.         Tk_FreeBitmap(wmPtr->hints.icon_pixmap);
  736.         }
  737.         wmPtr->hints.flags &= ~IconPixmapHint;
  738.     } else {
  739.         pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
  740.         if (pixmap == None) {
  741.         return TCL_ERROR;
  742.         }
  743.         wmPtr->hints.icon_pixmap = pixmap;
  744.         wmPtr->hints.flags |= IconPixmapHint;
  745.     }
  746.     UpdateHints(winPtr);
  747.     } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
  748.         && (length >= 5)) {
  749.     if (argc != 3) {
  750.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  751.             argv[0], " iconify window\"", (char *) NULL);
  752.         return TCL_ERROR;
  753.     }
  754.     wmPtr->hints.initial_state = IconicState;
  755.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  756.         return TCL_OK;
  757.     }
  758. #ifndef X11R3
  759.     if (XIconifyWindow(winPtr->display, winPtr->window,
  760.         winPtr->screenNum) == 0) {
  761.         interp->result =
  762.             "couldn't send iconify message to window manager";
  763.         return TCL_ERROR;
  764.     }
  765. #else
  766.     interp->result = "can't iconify under X11R3";
  767.     return TCL_ERROR;
  768. #endif
  769.     } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
  770.         && (length >= 5)) {
  771.     Pixmap pixmap;
  772.  
  773.     if ((argc != 3) && (argc != 4)) {
  774.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  775.             argv[0], " iconmask window ?bitmap?\"",
  776.             (char *) NULL);
  777.         return TCL_ERROR;
  778.     }
  779.     if (argc == 3) {
  780.         if (wmPtr->hints.flags & IconMaskHint) {
  781.         interp->result = Tk_NameOfBitmap(wmPtr->hints.icon_mask);
  782.         }
  783.         return TCL_OK;
  784.     }
  785.     if (*argv[3] == '\0') {
  786.         if (wmPtr->hints.icon_mask != None) {
  787.         Tk_FreeBitmap(wmPtr->hints.icon_mask);
  788.         }
  789.         wmPtr->hints.flags &= ~IconMaskHint;
  790.     } else {
  791.         pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
  792.         if (pixmap == None) {
  793.         return TCL_ERROR;
  794.         }
  795.         wmPtr->hints.icon_mask = pixmap;
  796.         wmPtr->hints.flags |= IconMaskHint;
  797.     }
  798.     UpdateHints(winPtr);
  799.     } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
  800.         && (length >= 5)) {
  801.     if (argc > 4) {
  802.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  803.             argv[0], " iconname window ?newName?\"", (char *) NULL);
  804.         return TCL_ERROR;
  805.     }
  806.     if (argc == 3) {
  807.         interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";
  808.         return TCL_OK;
  809.     } else {
  810.         wmPtr->iconName = Tk_GetUid(argv[3]);
  811.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  812.         XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
  813.         }
  814.     }
  815.     } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
  816.         && (length >= 5)) {
  817.     int x, y;
  818.  
  819.     if ((argc != 3) && (argc != 5)) {
  820.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  821.             argv[0], " iconposition window ?x y?\"",
  822.             (char *) NULL);
  823.         return TCL_ERROR;
  824.     }
  825.     if (argc == 3) {
  826.         if (wmPtr->hints.flags & IconPositionHint) {
  827.         sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,
  828.             wmPtr->hints.icon_y);
  829.         }
  830.         return TCL_OK;
  831.     }
  832.     if (*argv[3] == '\0') {
  833.         wmPtr->hints.flags &= ~IconPositionHint;
  834.     } else {
  835.         if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
  836.             || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
  837.         return TCL_ERROR;
  838.         }
  839.         wmPtr->hints.icon_x = x;
  840.         wmPtr->hints.icon_y = y;
  841.         wmPtr->hints.flags |= IconPositionHint;
  842.     }
  843.     UpdateHints(winPtr);
  844.     } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
  845.         && (length >= 5)) {
  846.     Tk_Window tkwin2;
  847.  
  848.     if ((argc != 3) && (argc != 4)) {
  849.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  850.             argv[0], " iconwindow window ?pathName?\"",
  851.             (char *) NULL);
  852.         return TCL_ERROR;
  853.     }
  854.     if (argc == 3) {
  855.         if (wmPtr->hints.flags & IconWindowHint) {
  856.         interp->result = wmPtr->iconWindowName;
  857.         }
  858.         return TCL_OK;
  859.     }
  860.     if (*argv[3] == '\0') {
  861.         wmPtr->hints.flags &= ~IconWindowHint;
  862.         wmPtr->iconWindowName = NULL;
  863.     } else {
  864.         tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
  865.         if (tkwin2 == NULL) {
  866.         return TCL_ERROR;
  867.         }
  868.         Tk_MakeWindowExist(tkwin2);
  869.         wmPtr->hints.icon_window = Tk_WindowId(tkwin2);
  870.         wmPtr->hints.flags |= IconWindowHint;
  871.         wmPtr->iconWindowName = Tk_PathName(tkwin2);
  872.     }
  873.     UpdateHints(winPtr);
  874.     } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
  875.         && (length >= 2)) {
  876.     int width, height;
  877.     if ((argc != 3) && (argc != 5)) {
  878.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  879.             argv[0], " maxsize window ?width height?\"", (char *) NULL);
  880.         return TCL_ERROR;
  881.     }
  882.     if (argc == 3) {
  883.         if (wmPtr->sizeHintsFlags & PMaxSize) {
  884.         sprintf(interp->result, "%d %d", wmPtr->maxWidth,
  885.             wmPtr->maxHeight);
  886.         }
  887.         return TCL_OK;
  888.     }
  889.     if (*argv[3] == '\0') {
  890.         wmPtr->sizeHintsFlags &= ~PMaxSize;
  891.     } else {
  892.         if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
  893.             || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
  894.         return TCL_ERROR;
  895.         }
  896.         wmPtr->maxWidth = width;
  897.         wmPtr->maxHeight = height;
  898.         wmPtr->sizeHintsFlags |= PMaxSize;
  899.     }
  900.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  901.     goto updateGeom;
  902.     } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
  903.         && (length >= 2)) {
  904.     int width, height;
  905.     if ((argc != 3) && (argc != 5)) {
  906.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  907.             argv[0], " minsize window ?width height?\"", (char *) NULL);
  908.         return TCL_ERROR;
  909.     }
  910.     if (argc == 3) {
  911.         if (wmPtr->sizeHintsFlags & PMinSize) {
  912.         sprintf(interp->result, "%d %d", wmPtr->minWidth,
  913.             wmPtr->minHeight);
  914.         }
  915.         return TCL_OK;
  916.     }
  917.     if (*argv[3] == '\0') {
  918.         wmPtr->sizeHintsFlags &= ~PMinSize;
  919.     } else {
  920.         if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
  921.             || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
  922.         return TCL_ERROR;
  923.         }
  924.         wmPtr->minWidth = width;
  925.         wmPtr->minHeight = height;
  926.         wmPtr->sizeHintsFlags |= PMinSize;
  927.     }
  928.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  929.     goto updateGeom;
  930.     } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)) {
  931.     if ((argc != 3) && (argc != 4)) {
  932.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  933.             argv[0], " positionfrom window ?user/program?\"",
  934.             (char *) NULL);
  935.         return TCL_ERROR;
  936.     }
  937.     if (argc == 3) {
  938.         if (wmPtr->sizeHintsFlags & USPosition) {
  939.         interp->result = "user";
  940.         } else if (wmPtr->sizeHintsFlags & PPosition) {
  941.         interp->result = "program";
  942.         }
  943.         return TCL_OK;
  944.     }
  945.     if (*argv[3] == '\0') {
  946.         wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
  947.     } else {
  948.         c = argv[3][0];
  949.         length = strlen(argv[3]);
  950.         if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
  951.         wmPtr->sizeHintsFlags &= ~PPosition;
  952.         wmPtr->sizeHintsFlags |= USPosition;
  953.         } else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) {
  954.         wmPtr->sizeHintsFlags &= ~USPosition;
  955.         wmPtr->sizeHintsFlags |= PPosition;
  956.         } else {
  957.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  958.             "\": must be program or user", (char *) NULL);
  959.         return TCL_ERROR;
  960.         }
  961.     }
  962.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  963.     goto updateGeom;
  964.     } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)) {
  965.     if ((argc != 3) && (argc != 4)) {
  966.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  967.             argv[0], " sizefrom window ?user|program?\"",
  968.             (char *) NULL);
  969.         return TCL_ERROR;
  970.     }
  971.     if (argc == 3) {
  972.         if (wmPtr->sizeHintsFlags & USSize) {
  973.         interp->result = "user";
  974.         } else if (wmPtr->sizeHintsFlags & PSize) {
  975.         interp->result = "program";
  976.         }
  977.         return TCL_OK;
  978.     }
  979.     if (*argv[3] == '\0') {
  980.         wmPtr->sizeHintsFlags &= ~(USSize|PSize);
  981.     } else {
  982.         c = argv[3][0];
  983.         length = strlen(argv[3]);
  984.         if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
  985.         wmPtr->sizeHintsFlags &= ~PSize;
  986.         wmPtr->sizeHintsFlags |= USSize;
  987.         } else if ((c == 'p')
  988.             && (strncmp(argv[3], "program", length) == 0)) {
  989.         wmPtr->sizeHintsFlags &= ~USSize;
  990.         wmPtr->sizeHintsFlags |= PSize;
  991.         } else {
  992.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  993.             "\": must be program or user", (char *) NULL);
  994.         return TCL_ERROR;
  995.         }
  996.     }
  997.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  998.     goto updateGeom;
  999.     } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
  1000.         && (length >= 2)) {
  1001.     if (argc > 4) {
  1002.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1003.             argv[0], " title window ?newTitle?\"", (char *) NULL);
  1004.         return TCL_ERROR;
  1005.     }
  1006.     if (argc == 3) {
  1007.         interp->result = (wmPtr->titleUid != NULL) ? wmPtr->titleUid
  1008.             : winPtr->nameUid;
  1009.         return TCL_OK;
  1010.     } else {
  1011.         wmPtr->titleUid = Tk_GetUid(argv[3]);
  1012. #ifndef X11R3
  1013.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1014.         XTextProperty textProp;
  1015.  
  1016.         if (XStringListToTextProperty(&wmPtr->titleUid, 1,
  1017.             &textProp)  != 0) {
  1018.             XSetWMName(winPtr->display, winPtr->window, &textProp);
  1019.             XFree((char *) textProp.value);
  1020.         }
  1021.         }
  1022. #endif
  1023.     }
  1024. #ifndef X11R3
  1025.     } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
  1026.         && (length >= 2)) {
  1027.     Tk_Window master;
  1028.  
  1029.     if ((argc != 3) && (argc != 4)) {
  1030.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1031.             argv[0], " transient window ?master?\"", (char *) NULL);
  1032.         return TCL_ERROR;
  1033.     }
  1034.     if (argc == 3) {
  1035.         if (wmPtr->master != None) {
  1036.         interp->result = wmPtr->masterWindowName;
  1037.         }
  1038.         return TCL_OK;
  1039.     }
  1040.     if (argv[3][0] == '\0') {
  1041.         wmPtr->master = None;
  1042.         wmPtr->masterWindowName = NULL;
  1043.     } else {
  1044.         master = Tk_NameToWindow(interp, argv[3], tkwin);
  1045.         if (master == NULL) {
  1046.         return TCL_ERROR;
  1047.         }
  1048.         Tk_MakeWindowExist(master);
  1049.         wmPtr->master = Tk_WindowId(master);
  1050.         wmPtr->masterWindowName = Tk_PathName(master);
  1051.     }
  1052.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1053.         XSetTransientForHint(winPtr->display, winPtr->window,
  1054.             wmPtr->master);
  1055.     }
  1056.     } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) {
  1057.     if (argc != 3) {
  1058.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1059.             argv[0], " withdraw window\"", (char *) NULL);
  1060.         return TCL_ERROR;
  1061.     }
  1062.     wmPtr->hints.initial_state = WithdrawnState;
  1063.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1064.         return TCL_OK;
  1065.     }
  1066.     if (XWithdrawWindow(winPtr->display, winPtr->window,
  1067.         winPtr->screenNum) == 0) {
  1068.         interp->result =
  1069.             "couldn't send withdraw message to window manager";
  1070.         return TCL_ERROR;
  1071.     }
  1072.     winPtr->flags &= ~TK_MAPPED;
  1073. #endif
  1074.     } else {
  1075.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  1076.         "\": must be aspect, deiconify, focusmodel, ",
  1077.         "geometry, grid, group, iconbitmap, ",
  1078.         "iconify, iconmask, iconname, iconposition, ",
  1079.         "iconwindow, maxsize, minsize, positionfrom, ",
  1080.         "sizefrom,  title, transient, or withdraw",
  1081.         (char *) NULL);
  1082.     return TCL_ERROR;
  1083.     }
  1084.     return TCL_OK;
  1085.  
  1086.     updateGeom:
  1087.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1088.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1089.     wmPtr->flags |= WM_UPDATE_PENDING;
  1090.     }
  1091.     return TCL_OK;
  1092. }
  1093.  
  1094. /*
  1095.  *----------------------------------------------------------------------
  1096.  *
  1097.  * Tk_SetGrid --
  1098.  *
  1099.  *    This procedure is invoked by a widget when it wishes to set a grid
  1100.  *    coordinate system that controls the size of a top-level window.
  1101.  *    It provides a C interface equivalent to the "wm grid" command and
  1102.  *    is usually asscoiated with the -setgrid option.
  1103.  *
  1104.  * Results:
  1105.  *    None.
  1106.  *
  1107.  * Side effects:
  1108.  *    Grid-related information will be passed to the window manager, so
  1109.  *    that the top-level window associated with tkwin will resize on
  1110.  *    even grid units.
  1111.  *
  1112.  *----------------------------------------------------------------------
  1113.  */
  1114.  
  1115. void
  1116. Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
  1117.     Tk_Window tkwin;        /* Token for window.  New window mgr info
  1118.                  * will be posted for the top-level window
  1119.                  * associated with this window. */
  1120.     int reqWidth;        /* Width (in grid units) corresponding to
  1121.                  * the requested geometry for tkwin. */
  1122.     int reqHeight;        /* Height (in grid units) corresponding to
  1123.                  * the requested geometry for tkwin. */
  1124.     int widthInc, heightInc;    /* Pixel increments corresponding to a
  1125.                  * change of one grid unit. */
  1126. {
  1127.     TkWindow *winPtr = (TkWindow *) tkwin;
  1128.     register WmInfo *wmPtr;
  1129.  
  1130.     /*
  1131.      * Find the top-level window for tkwin, plus the window manager
  1132.      * information.
  1133.      */
  1134.  
  1135.     while (!(winPtr->flags & TK_TOP_LEVEL)) {
  1136.     winPtr = winPtr->parentPtr;
  1137.     }
  1138.     wmPtr = winPtr->wmInfoPtr;
  1139.  
  1140.     if ((wmPtr->reqGridWidth == reqWidth)
  1141.         && (wmPtr->reqGridHeight != reqHeight)
  1142.         && (wmPtr->widthInc != widthInc)
  1143.         && (wmPtr->heightInc != heightInc)
  1144.         && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
  1145.             == PBaseSize|PResizeInc)) {
  1146.     return;
  1147.     }
  1148.  
  1149.     /*
  1150.      * If gridding was previously off, then forget about any window
  1151.      * size requests made by the user or via "wm geometry":  these are
  1152.      * in pixel units and there's no easy way to translate them to
  1153.      * grid units since the new requested size of the top-level window in
  1154.      * pixels may not yet have been registered yet (it may filter up
  1155.      * the hierarchy in DoWhenIdle handlers).
  1156.      */
  1157.  
  1158.     if (!(wmPtr->sizeHintsFlags & PBaseSize)) {
  1159.     wmPtr->width = -1;
  1160.     wmPtr->height = -1;
  1161.     }
  1162.  
  1163.     /* 
  1164.      * Set the new gridding information, and start the process of passing
  1165.      * all of this information to the window manager.
  1166.      */
  1167.  
  1168.     wmPtr->reqGridWidth = reqWidth;
  1169.     wmPtr->reqGridHeight = reqHeight;
  1170.     wmPtr->widthInc = widthInc;
  1171.     wmPtr->heightInc = heightInc;
  1172.     wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
  1173.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1174.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1175.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1176.     wmPtr->flags |= WM_UPDATE_PENDING;
  1177.     }
  1178. }
  1179.  
  1180. /*
  1181.  *----------------------------------------------------------------------
  1182.  *
  1183.  * TopLevelEventProc --
  1184.  *
  1185.  *    This procedure is invoked when a top-level (or other externally-
  1186.  *    managed window) is restructured in any way.
  1187.  *
  1188.  * Results:
  1189.  *    None.
  1190.  *
  1191.  * Side effects:
  1192.  *    Tk's internal data structures for the window get modified to
  1193.  *    reflect the structural change.
  1194.  *
  1195.  *----------------------------------------------------------------------
  1196.  */
  1197.  
  1198. static void
  1199. TopLevelEventProc(clientData, eventPtr)
  1200.     ClientData clientData;        /* Window for which event occurred. */
  1201.     XEvent *eventPtr;            /* Event that just happened. */
  1202. {
  1203.     register TkWindow *winPtr = (TkWindow *) clientData;
  1204.  
  1205.     if (eventPtr->type == DestroyNotify) {
  1206.     if (!(winPtr->flags & TK_ALREADY_DEAD)) {
  1207.         Tk_DestroyWindow((Tk_Window) winPtr);
  1208.     }
  1209.     } else if (eventPtr->type == ConfigureNotify) {
  1210.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1211.     int diff, x, y;
  1212.  
  1213.     /*
  1214.      * A top-level window has been reconfigured.  Problem #1:
  1215.      * discard stale information.  If the application has recently
  1216.      * tried to reconfigure itself, ignore all events until the
  1217.      * response to that reconfiguration arrives (the response is
  1218.      * assumed to be the first ConfigureNotify that arrives after
  1219.      * the server has seen the request;  this suffers from potential
  1220.      * races with user actions, but it's the best I can think of
  1221.      * right now).
  1222.      */
  1223.  
  1224.     diff = eventPtr->xconfigure.serial - wmPtr->configRequest;
  1225.     if (diff < 0) {
  1226.         return;
  1227.     }
  1228.  
  1229.     /*
  1230.      * Problem #2: reparenting window managers.  If the window
  1231.      * manager reparents a top-level window then the x and y
  1232.      * information that comes in events for the window is wrong:
  1233.      * it gives the location of the window inside its decorative
  1234.      * parent, rather than the location of the window in root
  1235.      * coordinates, which is what we want.  Window managers
  1236.      * are supposed to send synthetic events with the correct
  1237.      * information, but ICCCM doesn't require them to do this
  1238.      * under all conditions, and the information provided doesn't
  1239.      * include everything we need here.  So, the code below
  1240.      * maintains a bunch of information about the parent window.
  1241.      * If the window hasn't been reparented, we pretend that
  1242.      * there is a parent shrink-wrapped around the window.
  1243.      */
  1244.  
  1245.     if (wmPtr->reparent == None) {
  1246.         noReparent:
  1247.         winPtr->changes.x = eventPtr->xconfigure.x;
  1248.         winPtr->changes.y = eventPtr->xconfigure.y;
  1249.         wmPtr->parentWidth = eventPtr->xconfigure.width
  1250.             + 2*eventPtr->xconfigure.border_width;
  1251.         wmPtr->parentHeight = eventPtr->xconfigure.height
  1252.             + 2*eventPtr->xconfigure.border_width;
  1253.     } else {
  1254.         unsigned int width, height, bd, dummy;
  1255.         Window dummy2;
  1256.         Status status;
  1257.         Tk_ErrorHandler handler;
  1258.  
  1259.         handler = Tk_CreateErrorHandler(winPtr->display, BadDrawable, -1,
  1260.             -1, (Tk_ErrorProc *) NULL, (ClientData) NULL);
  1261.         status = XGetGeometry(winPtr->display, wmPtr->reparent,
  1262.             &dummy2, &x, &y, &width, &height, &bd, &dummy);
  1263.         Tk_DeleteErrorHandler(handler);
  1264.         if (status == 0) {
  1265.         /*
  1266.          * It appears that the reparented parent went away and
  1267.          * no-one told us.  Reset the window to indicate that
  1268.          * it's not reparented, then handle it as a non-reparented
  1269.          * window.
  1270.          */
  1271.         wmPtr->reparent = None;
  1272.         wmPtr->flags &= ~WM_NESTED_REPARENT;
  1273.         wmPtr->xInParent = wmPtr->yInParent = 0;
  1274.         goto noReparent;
  1275.         }
  1276.         wmPtr->parentWidth = width + 2*bd;
  1277.         wmPtr->parentHeight = height + 2*bd;
  1278.         winPtr->changes.x = x;
  1279.         winPtr->changes.y = y;
  1280.         if (wmPtr->flags & WM_NESTED_REPARENT) {
  1281.         int xOffset, yOffset;
  1282.  
  1283.         (void) XTranslateCoordinates(winPtr->display, winPtr->window,
  1284.             wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
  1285.         wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width;
  1286.         wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width;
  1287.         } else {
  1288.         if (!eventPtr->xconfigure.send_event) {
  1289.             wmPtr->xInParent = eventPtr->xconfigure.x + bd;
  1290.             wmPtr->yInParent = eventPtr->xconfigure.y + bd;
  1291.         }
  1292.         }
  1293.         winPtr->changes.x = x + wmPtr->xInParent;
  1294.         winPtr->changes.y = y + wmPtr->yInParent;
  1295.     }
  1296.  
  1297.     /*
  1298.      * Problem #3: if the window size or location was changed
  1299.      * externally, update the geometry information in wmPtr to make
  1300.      * it look just as if the user had typed a "wm geometry" command
  1301.      * to make the change.  There are many tricky situations to deal
  1302.      * with:
  1303.      * (a) the event is simply a reflection of an internal geometry
  1304.      *     request from the window's widgets (must leave width and
  1305.      *     height alone in this case).
  1306.      * (b) the window manager might respond to a size request from
  1307.      *     us with a different size than requested (e.g. it might
  1308.      *     have a minimum allowable window size).  Because of this,
  1309.      *     can't just compare new size with requested size to determine
  1310.      *     whether this event is a reflection of an internal request
  1311.      *     from within the application.  Use WM_CONFIG_PENDING flag
  1312.      *     instead.
  1313.      * (c) ConfigureNotify events also arise if the window has been
  1314.      *     moved, even if its size hasn't changed.  Must distinguish
  1315.      *     between the user moving the window and the user resizing
  1316.      *     the window.
  1317.      */
  1318.  
  1319.     if (wmPtr->flags & WM_CONFIG_PENDING) {
  1320.         int diff;
  1321.         /*
  1322.          * Size change is just a reflection of something coming from
  1323.          * application.
  1324.          */
  1325.  
  1326.         diff = eventPtr->xconfigure.serial - wmPtr->configRequest;
  1327.         if (diff >= 0) {
  1328.         if (wmPtr->flags & WM_CONFIG_AGAIN) {
  1329.             if (!(wmPtr->flags & WM_UPDATE_PENDING)) {
  1330.             Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1331.             wmPtr->flags |= WM_UPDATE_PENDING;
  1332.             }
  1333.         }
  1334.         wmPtr->flags &= ~(WM_CONFIG_PENDING|WM_CONFIG_AGAIN);
  1335.         }
  1336.     } else if ((winPtr->changes.width != eventPtr->xconfigure.width)
  1337.         || (winPtr->changes.height != eventPtr->xconfigure.height)) {
  1338.         wmPtr->configWidth = -1;
  1339.         wmPtr->configHeight = -1;
  1340.         if (IS_GRIDDED(wmPtr)) {
  1341.         wmPtr->width = wmPtr->reqGridWidth
  1342.             + (eventPtr->xconfigure.width
  1343.             - winPtr->reqWidth)/wmPtr->widthInc;
  1344.         if (wmPtr->width < 0) {
  1345.             wmPtr->width = 0;
  1346.         }
  1347.         wmPtr->height = wmPtr->reqGridHeight
  1348.             + (eventPtr->xconfigure.height
  1349.             - winPtr->reqHeight)/wmPtr->heightInc;
  1350.         if (wmPtr->height < 0) {
  1351.             wmPtr->height = 0;
  1352.         }
  1353.         } else if ((eventPtr->xconfigure.width != winPtr->changes.width)
  1354.             || (eventPtr->xconfigure.height
  1355.                 != winPtr->changes.height)) {
  1356.         /*
  1357.          * The check above is needed so we don't think the user
  1358.          * requested a new size when all he/she did was to move
  1359.          * the window.
  1360.          */
  1361.  
  1362.         wmPtr->width = eventPtr->xconfigure.width;
  1363.         wmPtr->height = eventPtr->xconfigure.height;
  1364.         }
  1365.     }
  1366.  
  1367.     winPtr->changes.width = eventPtr->xconfigure.width;
  1368.     winPtr->changes.height = eventPtr->xconfigure.height;
  1369.     winPtr->changes.border_width = eventPtr->xconfigure.border_width;
  1370.     winPtr->changes.sibling = eventPtr->xconfigure.above;
  1371.     winPtr->changes.stack_mode = Above;
  1372.  
  1373.     x = winPtr->changes.x - wmPtr->xInParent;
  1374.     if (wmPtr->flags & WM_NEGATIVE_X) {
  1375.         x = DisplayWidth(winPtr->display, winPtr->screenNum)
  1376.             - (x + wmPtr->parentWidth);
  1377.     }
  1378.     y = winPtr->changes.y - wmPtr->yInParent;
  1379.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  1380.         y = DisplayHeight(winPtr->display, winPtr->screenNum)
  1381.             - (y + wmPtr->parentHeight);
  1382.     }
  1383.     if ((x != wmPtr->x) || (y != wmPtr->y)) {
  1384.         wmPtr->x = x;
  1385.         wmPtr->y = y;
  1386.     }
  1387.     } else if (eventPtr->type == MapNotify) {
  1388.     winPtr->flags |= TK_MAPPED;
  1389.     } else if (eventPtr->type == UnmapNotify) {
  1390.     winPtr->flags &= ~TK_MAPPED;
  1391.     } else if (eventPtr->type == ReparentNotify) {
  1392.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  1393.     Window root, *children, dummy2, *virtualRootPtr;
  1394.     Atom virtualRootAtom, actualType;
  1395.     int actualFormat;
  1396.     unsigned long numItems, bytesAfter;
  1397.     unsigned int dummy;
  1398.  
  1399.     /*
  1400.      * Locate the ancestor of this window that is just below the
  1401.      * root window for the screen (could be the window itself).
  1402.      * This code is a bit tricky because it allows for the
  1403.      * possibility of a virtual root window, which is identified
  1404.      * with a property named __SWM_VROOT.
  1405.      */
  1406.  
  1407.     virtualRootAtom = Tk_InternAtom((Tk_Window) winPtr, "__SWM_VROOT");
  1408.     wmPtr->flags &= ~WM_NESTED_REPARENT;
  1409.     wmPtr->reparent = None;
  1410.     root = eventPtr->xreparent.parent;
  1411.     while (root != RootWindow(winPtr->display, winPtr->screenNum)) {
  1412.         virtualRootPtr = NULL;
  1413.         if (XGetWindowProperty(winPtr->display, root, virtualRootAtom,
  1414.             0, (long) 1, False, XA_WINDOW, &actualType, &actualFormat,
  1415.             &numItems, &bytesAfter, (unsigned char **) &virtualRootPtr)
  1416.             == Success) {
  1417.         if (virtualRootPtr != NULL) {
  1418.             if (*virtualRootPtr != root) {
  1419.             panic("TopLevelEventProc confused over virtual root");
  1420.             }
  1421.             XFree((char *) virtualRootPtr);
  1422.             break;
  1423.         }
  1424.         }
  1425.         wmPtr->reparent = root;
  1426.         (void) XQueryTree(winPtr->display, root, &dummy2, &root,
  1427.             &children, &dummy);
  1428.         XFree((char *) children);
  1429.     }
  1430.  
  1431.     /*
  1432.      * The ancestor just below the (virtual) root is in wmPtr->reparent
  1433.      * now, and the (virtual) root is in root.
  1434.      */
  1435.  
  1436.  
  1437.     if (eventPtr->xreparent.parent == root) {
  1438.         wmPtr->reparent = None;
  1439.         wmPtr->flags &= ~WM_NESTED_REPARENT;
  1440.         wmPtr->parentWidth = winPtr->changes.width
  1441.             + 2*winPtr->changes.border_width;
  1442.         wmPtr->parentHeight = winPtr->changes.height
  1443.             + 2*winPtr->changes.border_width;
  1444.         wmPtr->xInParent = wmPtr->yInParent = 0;
  1445.         winPtr->changes.x = eventPtr->xreparent.x;
  1446.         winPtr->changes.y = eventPtr->xreparent.y;
  1447.     } else {
  1448.         int x, y, xOffset, yOffset;
  1449.         unsigned int width, height, bd;
  1450.  
  1451.         if (wmPtr->reparent != eventPtr->xreparent.parent) {
  1452.         wmPtr->flags |= WM_NESTED_REPARENT;
  1453.         } else {
  1454.         wmPtr->flags &= ~WM_NESTED_REPARENT;
  1455.         }
  1456.  
  1457.         /*
  1458.          * Compute and save information about reparent and about
  1459.          * the window's position in reparent.
  1460.          */
  1461.  
  1462.         (void) XGetGeometry(winPtr->display, wmPtr->reparent,
  1463.             &dummy2, &x, &y, &width, &height, &bd, &dummy);
  1464.         wmPtr->parentWidth = width + 2*bd;
  1465.         wmPtr->parentHeight = height + 2*bd;
  1466.         (void) XTranslateCoordinates(winPtr->display, winPtr->window,
  1467.             wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
  1468.         wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width;
  1469.         wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width;
  1470.         winPtr->changes.x = x + xOffset;
  1471.         winPtr->changes.y = y + yOffset;
  1472.     }
  1473.     } else if ((eventPtr->type == EnterNotify)
  1474.         || (eventPtr->type == LeaveNotify)) {
  1475.     TkFocusEventProc(winPtr, eventPtr);
  1476.     }
  1477. }
  1478.  
  1479. /*
  1480.  *----------------------------------------------------------------------
  1481.  *
  1482.  * TopLevelReqProc --
  1483.  *
  1484.  *    This procedure is invoked by the geometry manager whenever
  1485.  *    the requested size for a top-level window is changed.
  1486.  *
  1487.  * Results:
  1488.  *    None.
  1489.  *
  1490.  * Side effects:
  1491.  *    Arrange for the window to be resized to satisfy the request
  1492.  *    (this happens as a when-idle action).
  1493.  *
  1494.  *----------------------------------------------------------------------
  1495.  */
  1496.  
  1497.     /* ARGSUSED */
  1498. static void
  1499. TopLevelReqProc(dummy, tkwin)
  1500.     ClientData dummy;            /* Not used. */
  1501.     Tk_Window tkwin;            /* Information about window. */
  1502. {
  1503.     TkWindow *winPtr = (TkWindow *) tkwin;
  1504.     WmInfo *wmPtr;
  1505.  
  1506.     wmPtr = winPtr->wmInfoPtr;
  1507.     if ((wmPtr->prevReqWidth == winPtr->reqWidth)
  1508.         && (wmPtr->prevReqHeight == winPtr->reqHeight)) {
  1509.     return;
  1510.     }
  1511.     wmPtr->prevReqWidth = winPtr->reqWidth;
  1512.     wmPtr->prevReqHeight = winPtr->reqHeight;
  1513.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1514.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1515.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1516.     wmPtr->flags |= WM_UPDATE_PENDING;
  1517.     }
  1518. }
  1519.  
  1520. /*
  1521.  *----------------------------------------------------------------------
  1522.  *
  1523.  * UpdateGeometryInfo --
  1524.  *
  1525.  *    This procedure is invoked when a top-level window is first
  1526.  *    mapped, and also as a when-idle procedure, to bring the
  1527.  *    geometry and/or position of a top-level window back into
  1528.  *    line with what has been requested by the user and/or widgets.
  1529.  *
  1530.  * Results:
  1531.  *    None.
  1532.  *
  1533.  * Side effects:
  1534.  *    The window's size and location may change, unless the WM prevents
  1535.  *    that from happening.
  1536.  *
  1537.  *----------------------------------------------------------------------
  1538.  */
  1539.  
  1540. static void
  1541. UpdateGeometryInfo(clientData)
  1542.     ClientData clientData;        /* Pointer to the window's record. */
  1543. {
  1544.     register TkWindow *winPtr = (TkWindow *) clientData;
  1545.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1546.     int x, y, width, height;
  1547.  
  1548.     /*
  1549.      * It isn't safe to issue a new reconfigure request while there is
  1550.      * another reconfigure request outstanding.  If this happens, skip
  1551.      * the second reconfigure operation but set a flag so it will get
  1552.      * done with the first one finishes.
  1553.      */
  1554.  
  1555.     wmPtr->flags &= ~WM_UPDATE_PENDING;
  1556.     if (wmPtr->flags & WM_CONFIG_PENDING) {
  1557.     wmPtr->flags |= WM_CONFIG_AGAIN;
  1558.     return;
  1559.     }
  1560.  
  1561.     /*
  1562.      * Compute the new size for the top-level window.  See the
  1563.      * user documentation for details on this, but the size
  1564.      * requested depends on (a) the size requested internally
  1565.      * by the window's widgets, (b) the size requested by the
  1566.      * user in a "wm geometry" command or via wm-based interactive
  1567.      * resizing (if any), and (c) whether or not the window
  1568.      * gridded.  Don't permit sizes <= 0 because this upsets
  1569.      * the X server.
  1570.      */
  1571.  
  1572.     if (wmPtr->width == -1) {
  1573.     width = winPtr->reqWidth;
  1574.     height = winPtr->reqHeight;
  1575.     } else if (IS_GRIDDED(wmPtr)) {
  1576.     width = winPtr->reqWidth
  1577.         + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
  1578.     height = winPtr->reqHeight
  1579.         + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
  1580.     } else {
  1581.     width = wmPtr->width;
  1582.     height = wmPtr->height;
  1583.     }
  1584.     if (width <= 0) {
  1585.     width = 1;
  1586.     }
  1587.     if (height <= 0) {
  1588.     height = 1;
  1589.     }
  1590.  
  1591.     /*
  1592.      * Compute the new position for the window.  This is tricky, because
  1593.      * we need to include the border widths supplied by a reparented
  1594.      * parent in this calculation, but can't use the parent's current
  1595.      * overall size since that may change as a result of this code.
  1596.      */
  1597.  
  1598.     if (wmPtr->flags & WM_NEGATIVE_X) {
  1599.     x = DisplayWidth(winPtr->display, winPtr->screenNum) - wmPtr->x
  1600.         - (width + (wmPtr->parentWidth - winPtr->changes.width))
  1601.         + wmPtr->xInParent;
  1602.     } else {
  1603.     x =  wmPtr->x + wmPtr->xInParent;
  1604.     }
  1605.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  1606.     y = DisplayHeight(winPtr->display, winPtr->screenNum) - wmPtr->y
  1607.         - (height + (wmPtr->parentHeight - winPtr->changes.height))
  1608.         + wmPtr->yInParent;
  1609.     } else {
  1610.     y =  wmPtr->y + wmPtr->yInParent;
  1611.     }
  1612.  
  1613.     /*
  1614.      * If the window's size is going to change and the window is
  1615.      * supposed to not be resizable by the user, then we have to
  1616.      * update the size hints.  There may also be a size-hint-update
  1617.      * request pending from somewhere else, too.
  1618.      */
  1619.  
  1620.     if (((width != winPtr->changes.width) || (width != winPtr->changes.width))
  1621.         && !IS_GRIDDED(wmPtr)
  1622.         && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
  1623.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1624.     }
  1625.     if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {
  1626.     UpdateSizeHints(winPtr);
  1627.     }
  1628.  
  1629.     /*
  1630.      * If the geometry hasn't changed, be careful to  use only a
  1631.      * resize operation.  This is because of bugs in some window
  1632.      * managers (e.g. twm, as of 4/24/91) where they don't interpret
  1633.      * coordinates according to ICCCM.
  1634.      */
  1635.  
  1636.     if ((x != winPtr->changes.x) || (y != winPtr->changes.y)) {
  1637.     wmPtr->configRequest = XNextRequest(winPtr->display);
  1638.     wmPtr->configWidth = width;
  1639.     wmPtr->configHeight = height;
  1640.     Tk_MoveResizeWindow((Tk_Window) winPtr, x, y, (unsigned) width,
  1641.         (unsigned) height);
  1642.     wmPtr->flags |= WM_CONFIG_PENDING;
  1643.     } else if ((width != wmPtr->configWidth)
  1644.         || (height != wmPtr->configHeight)) {
  1645.     wmPtr->configRequest = XNextRequest(winPtr->display);
  1646.     wmPtr->configWidth = width;
  1647.     wmPtr->configHeight = height;
  1648.     Tk_ResizeWindow((Tk_Window) winPtr, (unsigned) width,
  1649.         (unsigned) height);
  1650.     wmPtr->flags |= WM_CONFIG_PENDING;
  1651.     }
  1652. }
  1653.  
  1654. /*
  1655.  *--------------------------------------------------------------
  1656.  *
  1657.  * UpdateSizeHints --
  1658.  *
  1659.  *    This procedure is called to update the window manager's
  1660.  *    size hints information from the information in a WmInfo
  1661.  *    structure.
  1662.  *
  1663.  * Results:
  1664.  *    None.
  1665.  *
  1666.  * Side effects:
  1667.  *    Properties get changed for winPtr.
  1668.  *
  1669.  *--------------------------------------------------------------
  1670.  */
  1671.  
  1672. static void
  1673. UpdateSizeHints(winPtr)
  1674.     TkWindow *winPtr;
  1675. {
  1676.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1677.     XSizeHints *hintsPtr;
  1678.  
  1679.     wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;
  1680.  
  1681. #ifndef X11R3
  1682.     hintsPtr = XAllocSizeHints();
  1683.     if (hintsPtr == NULL) {
  1684.     return;
  1685.     }
  1686.  
  1687.     /*
  1688.      * Compute the pixel-based sizes for the various fields in the
  1689.      * size hints structure, based on the grid-based sizes in
  1690.      * our structure.
  1691.      */
  1692.  
  1693.     if (IS_GRIDDED(wmPtr)) {
  1694.     hintsPtr->base_width = winPtr->reqWidth
  1695.         - (wmPtr->reqGridWidth * wmPtr->widthInc);
  1696.     if (hintsPtr->base_width < 0) {
  1697.         hintsPtr->base_width = 0;
  1698.     }
  1699.     hintsPtr->base_height = winPtr->reqHeight
  1700.         - (wmPtr->reqGridHeight * wmPtr->heightInc);
  1701.     if (hintsPtr->base_height < 0) {
  1702.         hintsPtr->base_height = 0;
  1703.     }
  1704.     hintsPtr->min_width = hintsPtr->base_width
  1705.         + (wmPtr->minWidth * wmPtr->widthInc);
  1706.     hintsPtr->min_height = hintsPtr->base_height
  1707.         + (wmPtr->minHeight * wmPtr->heightInc);
  1708.     hintsPtr->max_width = hintsPtr->base_width
  1709.         + (wmPtr->maxWidth * wmPtr->widthInc);
  1710.     hintsPtr->max_height = hintsPtr->base_height
  1711.         + (wmPtr->maxHeight * wmPtr->heightInc);
  1712.     } else {
  1713.     hintsPtr->min_width = wmPtr->minWidth;
  1714.     hintsPtr->min_height = wmPtr->minHeight;
  1715.     hintsPtr->max_width = wmPtr->maxWidth;
  1716.     hintsPtr->max_height = wmPtr->maxHeight;
  1717.     hintsPtr->base_width = 0;
  1718.     hintsPtr->base_height = 0;
  1719.     }
  1720.     hintsPtr->width_inc = wmPtr->widthInc;
  1721.     hintsPtr->height_inc = wmPtr->heightInc;
  1722.     hintsPtr->min_aspect.x = wmPtr->minAspect.x;
  1723.     hintsPtr->min_aspect.y = wmPtr->minAspect.y;
  1724.     hintsPtr->max_aspect.x = wmPtr->maxAspect.x;
  1725.     hintsPtr->max_aspect.y = wmPtr->maxAspect.y;
  1726.     hintsPtr->win_gravity = wmPtr->gravity;
  1727.     hintsPtr->flags = wmPtr->sizeHintsFlags;
  1728.  
  1729.     /*
  1730.      * If a window is non-gridded and no minimum or maximum size has
  1731.      * been specified, don't let the window be resized at all.
  1732.      */
  1733.  
  1734.     if (!IS_GRIDDED(wmPtr)
  1735.         && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
  1736.     int width, height;
  1737.  
  1738.     width = wmPtr->width;
  1739.     height = wmPtr->height;
  1740.     if (width < 0) {
  1741.         width = winPtr->reqWidth;
  1742.         height = winPtr->reqHeight;
  1743.     }
  1744.     hintsPtr->min_width = hintsPtr->max_width = width;
  1745.     hintsPtr->min_height = hintsPtr->max_height = height;
  1746.     hintsPtr->flags |= PMinSize|PMaxSize;
  1747.     }
  1748.  
  1749.     /*
  1750.      * If min or max size isn't specified, fill in with extreme values
  1751.      * rather than leaving unspecified.  Otherwise window manager may
  1752.      * do someting counter-intuitive like the last value ever specified.
  1753.      */
  1754.  
  1755.     if (!(hintsPtr->flags & PMinSize)) {
  1756.     hintsPtr->min_width = hintsPtr->min_height = 0;
  1757.     hintsPtr->flags |= PMinSize;
  1758.     }
  1759.     if (!(hintsPtr->flags & PMaxSize)) {
  1760.     hintsPtr->max_width = hintsPtr->max_height = 1000000;
  1761.     hintsPtr->flags |= PMaxSize;
  1762.     }
  1763.  
  1764.     XSetWMNormalHints(winPtr->display, winPtr->window, hintsPtr);
  1765.  
  1766.     XFree((char *) hintsPtr);
  1767. #endif /* X11R3 */
  1768. }
  1769.  
  1770. /*
  1771.  *--------------------------------------------------------------
  1772.  *
  1773.  * UpdateHints --
  1774.  *
  1775.  *    This procedure is called to update the window manager's
  1776.  *    hints information from the information in a WmInfo
  1777.  *    structure.
  1778.  *
  1779.  * Results:
  1780.  *    None.
  1781.  *
  1782.  * Side effects:
  1783.  *    Properties get changed for winPtr.
  1784.  *
  1785.  *--------------------------------------------------------------
  1786.  */
  1787.  
  1788. static void
  1789. UpdateHints(winPtr)
  1790.     TkWindow *winPtr;
  1791. {
  1792.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  1793.  
  1794.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1795.     return;
  1796.     }
  1797.     XSetWMHints(winPtr->display, winPtr->window, &wmPtr->hints);
  1798. }
  1799.  
  1800. /*
  1801.  *--------------------------------------------------------------
  1802.  *
  1803.  * ParseGeometry --
  1804.  *
  1805.  *    This procedure parses a geometry string and updates
  1806.  *    information used to control the geometry of a top-level
  1807.  *    window.
  1808.  *
  1809.  * Results:
  1810.  *    A standard Tcl return value, plus an error message in
  1811.  *    interp->result if an error occurs.
  1812.  *
  1813.  * Side effects:
  1814.  *    The size and/or location of winPtr may change.
  1815.  *
  1816.  *--------------------------------------------------------------
  1817.  */
  1818.  
  1819. static int
  1820. ParseGeometry(interp, string, winPtr)
  1821.     Tcl_Interp *interp;        /* Used for error reporting. */
  1822.     char *string;    /* String containing new geometry.  Has the
  1823.                  * standard form "=wxh+x+y". */
  1824.     TkWindow *winPtr;        /* Pointer to top-level window whose
  1825.                  * geometry is to be changed. */
  1826. {
  1827.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1828.     int x, y, width, height, flags;
  1829.     char *end;
  1830.     register char *p = string;
  1831.  
  1832.     /*
  1833.      * The leading "=" is optional.
  1834.      */
  1835.  
  1836.     if (*p == '=') {
  1837.     p++;
  1838.     }
  1839.  
  1840.     /*
  1841.      * Parse the width and height, if they are present.  Don't
  1842.      * actually update any of the fields of wmPtr until we've
  1843.      * successfully parsed the entire geometry string.
  1844.      */
  1845.  
  1846.     width = wmPtr->width;
  1847.     height = wmPtr->height;
  1848.     x = wmPtr->x;
  1849.     y = wmPtr->y;
  1850.     flags = wmPtr->flags;
  1851.     if (isdigit(*p)) {
  1852.     width = strtoul(p, &end, 10);
  1853.     p = end;
  1854.     if (*p != 'x') {
  1855.         goto error;
  1856.     }
  1857.     p++;
  1858.     if (!isdigit(*p)) {
  1859.         goto error;
  1860.     }
  1861.     height = strtoul(p, &end, 10);
  1862.     p = end;
  1863.     }
  1864.  
  1865.     /*
  1866.      * Parse the X and Y coordinates, if they are present.
  1867.      */
  1868.  
  1869.     if (*p != '\0') {
  1870.     flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
  1871.     if (*p == '-') {
  1872.         flags |= WM_NEGATIVE_X;
  1873.     } else if (*p != '+') {
  1874.         goto error;
  1875.     }
  1876.     x = strtol(p+1, &end, 10);
  1877.     p = end;
  1878.     if (*p == '-') {
  1879.         flags |= WM_NEGATIVE_Y;
  1880.     } else if (*p != '+') {
  1881.         goto error;
  1882.     }
  1883.     y = strtol(p+1, &end, 10);
  1884.     if (*end != '\0') {
  1885.         goto error;
  1886.     }
  1887.  
  1888.     /*
  1889.      * Assume that the geometry information came from the user,
  1890.      * unless an explicit source has been specified.  Otherwise
  1891.      * most window managers assume that the size hints were
  1892.      * program-specified and they ignore them.
  1893.      */
  1894.  
  1895.     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
  1896.         wmPtr->sizeHintsFlags |= USPosition;
  1897.         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1898.     }
  1899.     }
  1900.  
  1901.     /*
  1902.      * Everything was parsed OK.  Update the fields of *wmPtr and
  1903.      * arrange for the appropriate information to be percolated out
  1904.      * to the window manager at the next idle moment.
  1905.      */
  1906.  
  1907.     wmPtr->width = width;
  1908.     wmPtr->height = height;
  1909.     wmPtr->x = x;
  1910.     wmPtr->y = y;
  1911.     wmPtr->flags = flags;
  1912.  
  1913.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1914.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1915.     wmPtr->flags |= WM_UPDATE_PENDING;
  1916.     }
  1917.     return TCL_OK;
  1918.  
  1919.     error:
  1920.     Tcl_AppendResult(interp, "bad geometry specifier \"",
  1921.         string, "\"", (char *) NULL);
  1922.     return TCL_ERROR;
  1923. }
  1924.  
  1925. /*
  1926.  *----------------------------------------------------------------------
  1927.  *
  1928.  * Tk_GetRootCoords --
  1929.  *
  1930.  *    Given a token for a window, this procedure traces through the
  1931.  *    window's lineage to find the root-window coordinates corresponding
  1932.  *    to point (0,0) in the window.
  1933.  *
  1934.  * Results:
  1935.  *    The locations pointed to by xPtr and yPtr are filled in with
  1936.  *    the root coordinates of the (0,0) point in tkwin.
  1937.  *
  1938.  * Side effects:
  1939.  *    None.
  1940.  *
  1941.  *----------------------------------------------------------------------
  1942.  */
  1943.  
  1944. void
  1945. Tk_GetRootCoords(tkwin, xPtr, yPtr)
  1946.     Tk_Window tkwin;        /* Token for window. */
  1947.     int *xPtr;            /* Where to store x-displacement of (0,0). */
  1948.     int *yPtr;            /* Where to store y-displacement of (0,0). */
  1949. {
  1950.     int x, y;
  1951.     register TkWindow *winPtr = (TkWindow *) tkwin;
  1952.  
  1953.     /*
  1954.      * Search back through this window's parents all the way to a
  1955.      * top-level window, combining the offsets of each window within
  1956.      * its parent.
  1957.      */
  1958.  
  1959.     x = y = 0;
  1960.     while (1) {
  1961.     x += winPtr->changes.x + winPtr->changes.border_width;
  1962.     y += winPtr->changes.y + winPtr->changes.border_width;
  1963.     if (winPtr->flags & TK_TOP_LEVEL) {
  1964.         break;
  1965.     }
  1966.     winPtr = winPtr->parentPtr;
  1967.     }
  1968.     *xPtr = x;
  1969.     *yPtr = y;
  1970. }
  1971.  
  1972. /*
  1973.  *----------------------------------------------------------------------
  1974.  *
  1975.  * Tk_CoordsToWindow --
  1976.  *
  1977.  *    Given the root coordinates of a point, this procedure
  1978.  *    returns the token for the top-most window covering that point,
  1979.  *    if there exists such a window in this application.
  1980.  *
  1981.  * Results:
  1982.  *    The return result is either a token for the window corresponding
  1983.  *    to rootX and rootY, or else NULL to indicate that there is no such
  1984.  *    window.
  1985.  *
  1986.  * Side effects:
  1987.  *    None.
  1988.  *
  1989.  *----------------------------------------------------------------------
  1990.  */
  1991.  
  1992. Tk_Window
  1993. Tk_CoordsToWindow(rootX, rootY, tkwin)
  1994.     int rootX, rootY;        /* Coordinates of point in root window. */
  1995.     Tk_Window tkwin;        /* Token for any window in application;
  1996.                  * used to identify the application. */
  1997. {
  1998.     Window rootChild, dummy3, dummy4;
  1999.     int i, dummy1, dummy2;
  2000.     register WmInfo *wmPtr;
  2001.     register TkWindow *winPtr, *childPtr;
  2002.     TkWindow *nextPtr;        /* Coordinates of highest child found so
  2003.                  * far that contains point. */
  2004.     int x, y;            /* Coordinates in winPtr. */
  2005.     int tmpx, tmpy, bd;
  2006.     Window *children;        /* Children of winPtr, or NULL. */
  2007.     unsigned int numChildren;    /* Size of children array. */
  2008.  
  2009.     /*
  2010.      * Step 1:  find the top-level window that contains the desired
  2011.      * coordinates.
  2012.      */
  2013.  
  2014.     if (XTranslateCoordinates(Tk_Display(tkwin),
  2015.         RootWindowOfScreen(Tk_Screen(tkwin)),
  2016.         RootWindowOfScreen(Tk_Screen(tkwin)), rootX, rootY, &dummy1,
  2017.         &dummy2, &rootChild) == False) {
  2018.     panic("Tk_CoordsToWindow get False return from XTranslateCoordinates");
  2019.     }
  2020.     for (wmPtr = firstWmPtr; ; wmPtr = wmPtr->nextPtr) {
  2021.     if (wmPtr == NULL) {
  2022.         return NULL;
  2023.     }
  2024.     if ((wmPtr->reparent == rootChild) || ((wmPtr->reparent == None)
  2025.         && (wmPtr->winPtr->window == rootChild))) {
  2026.         break;
  2027.     }
  2028.     }
  2029.     winPtr = wmPtr->winPtr;
  2030.     if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) {
  2031.     return NULL;
  2032.     }
  2033.  
  2034.     /*
  2035.      * Step 2: work down through the hierarchy underneath this window.
  2036.      * At each level, scan through all the children to see if any contain
  2037.      * the point.  If none do, then we're done.  If one does, then do the
  2038.      * same thing on that child.  If two or more do, then fetch enough
  2039.      * information from the window server to figure out which is on top,
  2040.      * and repeat on that child.
  2041.      */
  2042.  
  2043.     x = rootX;
  2044.     y = rootY;
  2045.     while (1) {
  2046.     x -= winPtr->changes.x;
  2047.     y -= winPtr->changes.y;
  2048.     nextPtr = NULL;
  2049.     children = NULL;
  2050.     for (childPtr = winPtr->childList; childPtr != NULL;
  2051.         childPtr = childPtr->nextPtr) {
  2052.         if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_LEVEL)) {
  2053.         continue;
  2054.         }
  2055.         tmpx = x - childPtr->changes.x;
  2056.         tmpy = y - childPtr->changes.y;
  2057.         bd = childPtr->changes.border_width;
  2058.         if ((tmpx < -bd) || (tmpy < -bd)
  2059.             || (tmpx >= (childPtr->changes.width + bd))
  2060.             || (tmpy >= (childPtr->changes.height + bd))) {
  2061.         continue;
  2062.         }
  2063.         if (nextPtr == NULL) {
  2064.         nextPtr = childPtr;
  2065.         continue;
  2066.         }
  2067.  
  2068.         /*
  2069.          * More than one child of same parent overlaps point.  Must
  2070.          * figure out which is on top.  Keep a cache of the stacking
  2071.          * order for winPtr to help with this, in case there are >2
  2072.          * children overlapping.
  2073.          */
  2074.  
  2075.         if (children == NULL) {
  2076.         if (XQueryTree(winPtr->display, winPtr->window, &dummy3,
  2077.             &dummy4, &children, &numChildren) == 0) {
  2078.             panic("Tk_CoordsToWindow get error return from XQueryTree");
  2079.         }
  2080.         }
  2081.         for (i = 0; i < numChildren; i++) {
  2082.         if (children[i] == childPtr->window) {
  2083.             break;
  2084.         }
  2085.         if (children[i] == nextPtr->window) {
  2086.             nextPtr = childPtr;
  2087.             break;
  2088.         }
  2089.         }
  2090.     }
  2091.     if (children != NULL) {
  2092.         XFree((char *) children);
  2093.     }
  2094.     if (nextPtr == NULL) {
  2095.         break;
  2096.     }
  2097.     winPtr = nextPtr;
  2098.     }
  2099.     return (Tk_Window) winPtr;
  2100. }
  2101.